package org.liujing.jeditplugin;

import java.io.*;
import java.util.*;
import java.util.logging.*;
import java.util.concurrent.*;

import org.liujing.jeditplugin.ui.*;
import org.liujing.jeditplugin.v2.*;

import org.gjt.sp.jedit.*;
import org.gjt.sp.jedit.msg.*;

public class PluginPanelFactory implements EBComponent{
	private static Logger log = Logger.getLogger( PluginPanelFactory.class.getName() );
	
	private ConcurrentHashMap<View, PluginPanel> elements = new ConcurrentHashMap();
	
	private ConcurrentLinkedQueue<PluginPanel> recycled = new ConcurrentLinkedQueue();
	
	public PluginPanelFactory(){

	}
	
	public void start()throws IOException, ClassNotFoundException{
		log.fine("start");
		JeditBufferController	bufferCtl = null;
		ProjectController		projectCtl = null;
		File serFile = new File(MyJeditPlugin.getInstance().getPluginHome(), "jedit_buffer.ser");
		if(serFile.exists()){
			try{
				ObjectInputStream in = new ObjectInputStream( new FileInputStream(
				serFile) );
				bufferCtl = (JeditBufferController) in.readObject();
				in.close();
			}catch(IOException ive){
				bufferCtl = new JeditBufferController();
				log.log(Level.WARNING, "", ive);
			}
		}else{
			bufferCtl = new JeditBufferController();
		}
		bufferCtl.init();
		bufferCtl.syncBufferList();
		
		serFile = new File(MyJeditPlugin.getInstance().getPluginHome(), "jedit_project.ser");
		if(serFile.exists()){
			try{
				ObjectInputStream in = new ObjectInputStream( new FileInputStream(
				serFile) );
				projectCtl = (ProjectController) in.readObject();
				in.close();
			}catch(IOException ive){
				projectCtl = new ProjectController();
				log.log(Level.WARNING, "", ive);
			}
		}else{
			projectCtl = new ProjectController();
		}
		projectCtl.setRootDir(MyJeditPlugin.getInstance().getPluginHome());
		bufferCtl.boundProjectEvent(projectCtl);
		PluginPanel p = new PluginPanel(bufferCtl, projectCtl);
		log.fine("started");
		recycle(p);
	}
	
	public void stop()throws IOException{
		Iterator<PluginPanel> it = elements.values().iterator();
		PluginPanel toCopy = it.next();
		JeditBufferController bufferCtl0 = toCopy.getController(JeditBufferController.class);
		ProjectController projectCtl0 = toCopy.getController(ProjectController.class);
		
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
			new File(MyJeditPlugin.getInstance().getPluginHome(), "jedit_buffer.ser"), false));
		out.writeObject(bufferCtl0);
		out.close();
		
		out = new ObjectOutputStream(new FileOutputStream(
			new File(MyJeditPlugin.getInstance().getPluginHome(), "jedit_project.ser"), false));
		out.writeObject(projectCtl0);
		out.close();
	}
	
	public PluginPanel get(View view)throws IOException, ClassNotFoundException{
		PluginPanel p = null;
		if(log.isLoggable(Level.FINE))
			log.fine("New view "+ view + "/" + view.hashCode());
		if( recycled.isEmpty() ){
			Iterator<PluginPanel> it = elements.values().iterator();
			PluginPanel toCopy = it.next();
			JeditBufferController bufferCtl0 = toCopy.getController(JeditBufferController.class);
			ProjectController projectCtl0 = toCopy.getController(ProjectController.class);
			
			JeditBufferController bufferCtl = deepCopy(bufferCtl0);
			bufferCtl.syncBufferList();
			ProjectController projectCtl = deepCopy(projectCtl0);
			bufferCtl.boundProjectEvent(projectCtl);
			p = new PluginPanel(bufferCtl, projectCtl);
		}else{
			p = recycled.poll();
			
		}
		p.getController(JeditBufferController.class).setJeditView(view);		
		elements.put(view, p);
		
		return p;
	}
	
	public void recycle(PluginPanel pluginPanel){
		recycled.add(pluginPanel);
	}
	
	public void handleMessage(EBMessage message)
	{
		//log.fine(message.toString());
		if(message instanceof BufferUpdate){
			
			BufferUpdate m=(BufferUpdate)message;
			
			JeditBufferController bufferCtl = null;
			if(m.getWhat().equals(BufferUpdate.CLOSED)){
				Iterator<PluginPanel> it = elements.values().iterator();
				while(it.hasNext()){
					bufferCtl = it.next().getController(JeditBufferController.class);
					bufferCtl.bufferClosed(m.getBuffer());
				}
			}
			else if(m.getWhat().equals(m.CREATED)){
				Iterator<PluginPanel> it = elements.values().iterator();
				while(it.hasNext()){
					bufferCtl = it.next().getController(JeditBufferController.class);
					bufferCtl.bufferCreated(m.getBuffer());
				}
			}
			else if(m.DIRTY_CHANGED.equals(m.getWhat())){
				Iterator<PluginPanel> it = elements.values().iterator();
				while(it.hasNext()){
					bufferCtl = it.next().getController(JeditBufferController.class);
					bufferCtl.bufferUpdated(m.getBuffer());
				}
			}else if(m.getWhat().equals(m.SAVED) ){
				log.fine("buffer saved");
				Iterator<PluginPanel> it = elements.values().iterator();
				while(it.hasNext()){
					bufferCtl = it.next().getController(JeditBufferController.class);
					bufferCtl.bufferUpdated(m.getBuffer());
				}
			}
		}
		else if(message instanceof EditPaneUpdate){
			EditPaneUpdate m=(EditPaneUpdate)message;
			if(m.BUFFER_CHANGED==m.getWhat()){
				JeditBufferController bufferCtl = elements.get( m.getEditPane().getView() ).getController(JeditBufferController.class);
				Buffer b=m.getEditPane().getBuffer();
				log.fine("open buffer");
				//select the active buffer
				bufferCtl.bufferActivated(b);
				
			}
		}
		else if(message instanceof ViewUpdate){
			
		}else if(message instanceof EditorStarted){
			log.fine("EditorStarted");
			PluginPanel p = recycled.peek();
			if(p == null){
				p = elements.values().iterator().next();
			}
			JeditBufferController bufferCtl = p.getController(JeditBufferController.class);
			bufferCtl.syncBufferList();
		}
	}
	
	private<T> T deepCopy(T sourceObject)throws IOException, ClassNotFoundException{
		ByteArrayOutputStream objBuf = new ByteArrayOutputStream();
		ObjectOutputStream out = new ObjectOutputStream(objBuf);
		out.writeObject(sourceObject);
		out.close();
		
		ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(objBuf.toByteArray()));
		T o = (T)objIn.readObject();
		objIn.close();
		return o;
	}
	
}
